// Type definitions for ESTree AST specification
// Project: https://github.com/estree/estree
// Definitions by: RReverser <https://github.com/RReverser>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

// This definition file follows a somewhat unusual format. ESTree allows
// runtime type checks based on the `type` parameter. In order to explain this
// to typescript we want to use discriminated union types:
// https://github.com/Microsoft/TypeScript/pull/9163
//
// For ESTree this is a bit tricky because the high level interfaces like
// Node or Function are pulling double duty. We want to pass common fields down
// to the interfaces that extend them (like Identifier or
// ArrowFunctionExpression), but you can't extend a type union or enforce
// common fields on them. So we've split the high level interfaces into two
// types, a base type which passes down inherited fields, and a type union of
// all types which extend the base type. Only the type union is exported, and
// the union is how other types refer to the collection of inheriting types.
//
// This makes the definitions file here somewhat more difficult to maintain,
// but it has the notable advantage of making ESTree much easier to use as
// an end user.

interface BaseNodeWithoutComments {
  // Every leaf interface that extends BaseNode must specify a type property.
  // The type property should be a string literal. For example, Identifier
  // has: `type: "Identifier"`
  type: string;
  loc?: SourceLocation | null | undefined;
  range?: [number, number] | undefined;
}

interface BaseNode extends BaseNodeWithoutComments {
  leadingComments?: Array<Comment> | undefined;
  trailingComments?: Array<Comment> | undefined;
}

export type Node =
    Identifier | Literal | Program | Function | SwitchCase | CatchClause |
    VariableDeclarator | Statement | Expression | PrivateIdentifier | Property | PropertyDefinition |
    AssignmentProperty | Super | TemplateElement | SpreadElement | Pattern |
    ClassBody | Class | MethodDefinition | ModuleDeclaration | ModuleSpecifier;

export interface Comment extends BaseNodeWithoutComments {
  type: "Line" | "Block";
  value: string;
}

interface SourceLocation {
  source?: string | null | undefined;
  start: Position;
  end: Position;
}

export interface Position {
  /** >= 1 */
  line: number;
  /** >= 0 */
  column: number;
}

//REVIEW program comments
export interface Program extends BaseNode {
  type: "Program";
  sourceType: "script" | "module";
  body: Array<Directive | Statement | ModuleDeclaration>;
  comments?: Array<Comment> | undefined;
}

/**
 * e.g.  "use strict";
{
  "type": "Program",
  "body": [
    {
      "type": "ExpressionStatement",
      "expression": {
        "type": "Literal",
        "raw": "\"use strict\"",
        "value": "use strict",
        "range": [
          1,
          13
        ]
      },
      "range": [
        1,
        14
      ],
      "directive": "use strict"
    }
  ],
  "sourceType": "module",
  "range": [
    1,
    14
  ]
} 
 */
//TODO  Directive node not so necessary currently
export interface Directive extends BaseNode {
  type: "ExpressionStatement";
  expression: Literal;
  directive: string;
}

interface BaseFunction extends BaseNode {
  params: Array<Pattern>;
  generator?: boolean | undefined;
  async?: boolean | undefined;
  // The body is either BlockStatement or Expression because arrow functions
  // can have a body that's either. FunctionDeclarations and
  // FunctionExpressions have only BlockStatement bodies.
  body: BlockStatement | Expression;
}

export type Function =
    FunctionDeclaration | FunctionExpression | ArrowFunctionExpression;

export type Statement =
    ExpressionStatement | BlockStatement | EmptyStatement |
    DebuggerStatement | WithStatement | ReturnStatement | LabeledStatement |
    BreakStatement | ContinueStatement | IfStatement | SwitchStatement |
    ThrowStatement | TryStatement | WhileStatement | DoWhileStatement |
    ForStatement | ForInStatement | ForOfStatement | Declaration;

interface BaseStatement extends BaseNode { }

export interface EmptyStatement extends BaseStatement {
  type: "EmptyStatement";
}

export interface BlockStatement extends BaseStatement {
  type: "BlockStatement";
  body: Array<Statement>;
  innerComments?: Array<Comment> | undefined;
}

export interface ExpressionStatement extends BaseStatement {
  type: "ExpressionStatement";
  expression: Expression;
}

//REVIEW
export interface IfStatement extends BaseStatement {
  type: "IfStatement";
  test: Expression;
  consequent: Statement;
  alternate?: Statement | null | undefined;
}

//REVIEW
export interface LabeledStatement extends BaseStatement {
  type: "LabeledStatement";
  label: Identifier;
  body: Statement;
}

//REVIEW
export interface BreakStatement extends BaseStatement {
  type: "BreakStatement";
  label?: Identifier | null | undefined;
}

//REVIEW
export interface ContinueStatement extends BaseStatement {

  type: "ContinueStatement";
  label?: Identifier | null | undefined;
}

//REVIEW
export interface WithStatement extends BaseStatement {
  type: "WithStatement";
  object: Expression;
  body: Statement;
}
//REVIEW
export interface SwitchStatement extends BaseStatement {
  type: "SwitchStatement";
  discriminant: Expression;
  cases: Array<SwitchCase>;
}

//REVIEW
export interface ReturnStatement extends BaseStatement {
  type: "ReturnStatement";
  argument?: Expression | null | undefined;
}

//REVIEW
export interface ThrowStatement extends BaseStatement {
  type: "ThrowStatement";
  argument: Expression;
}

export interface TryStatement extends BaseStatement {
  type: "TryStatement";
  block: BlockStatement;
  handler?: CatchClause | null | undefined;
  finalizer?: BlockStatement | null | undefined;
}

//REVIEW
export interface WhileStatement extends BaseStatement {
  type: "WhileStatement";
  test: Expression;
  body: Statement;
}

//REVIEW
export interface DoWhileStatement extends BaseStatement {
  type: "DoWhileStatement";
  body: Statement;
  test: Expression;
}

//REVIEW
export interface ForStatement extends BaseStatement {
  type: "ForStatement";
  init?: VariableDeclaration | Expression | null | undefined;
  test?: Expression | null | undefined;
  update?: Expression | null | undefined;
  body: Statement;
}

//REVIEW
interface BaseForXStatement extends BaseStatement {
  left: VariableDeclaration | Pattern;
  right: Expression;
  body: Statement;
}

export interface ForInStatement extends BaseForXStatement {
  type: "ForInStatement";
}

export interface DebuggerStatement extends BaseStatement {
  type: "DebuggerStatement";
}

export type Declaration =
      FunctionDeclaration | VariableDeclaration | ClassDeclaration;

interface BaseDeclaration extends BaseStatement { }

//REVIEW in ANTLR baseFunction is replaced with call signature
export interface FunctionDeclaration extends BaseFunction, BaseDeclaration {
  type: "FunctionDeclaration";
  /** It is null when a function declaration is a part of the `export default function` statement */
  id: Identifier | null;
  body: BlockStatement;
}

//REVIEW VariableDeclaration
export interface VariableDeclaration extends BaseDeclaration {
  type: "VariableDeclaration";
  declarations: Array<VariableDeclarator>;
  kind: "var" | "let" | "const";
}

//REVIEW VariableDeclarator
export interface VariableDeclarator extends BaseNode {
  type: "VariableDeclarator";
  id: Pattern;
  init?: Expression | null | undefined;
}

type Expression =
    ThisExpression | ArrayExpression | ObjectExpression | FunctionExpression |
    ArrowFunctionExpression | YieldExpression | Literal | UnaryExpression |
    UpdateExpression | BinaryExpression | AssignmentExpression |
    LogicalExpression | MemberExpression | ConditionalExpression |
    CallExpression | NewExpression | SequenceExpression | TemplateLiteral |
    TaggedTemplateExpression | ClassExpression | MetaProperty | Identifier |
    AwaitExpression | ImportExpression | ChainExpression;

export interface BaseExpression extends BaseNode { }

type ChainElement = SimpleCallExpression | MemberExpression;

export interface ChainExpression extends BaseExpression {
  type: "ChainExpression";
  expression: ChainElement;
}

//REVIEW 
export interface ThisExpression extends BaseExpression {
  type: "ThisExpression";
}

//REVIEW  SpreadElement is replaced with  " Ellipsis? expression ','?  " in ANTLR
export interface ArrayExpression extends BaseExpression {
  type: "ArrayExpression";
  elements: Array<Expression | SpreadElement | null>;
}

//REVIEW
export interface ObjectExpression extends BaseExpression {
  type: "ObjectExpression";
  properties: Array<Property | SpreadElement>;
}

//TODO what does this node mean ??
export interface PrivateIdentifier extends BaseNode {
    type: "PrivateIdentifier";
    name: string;
}

//TODO what does shorthand mean ??
export interface Property extends BaseNode {
  type: "Property";
  key: Expression | PrivateIdentifier;
  value: Expression | Pattern; // Could be an AssignmentProperty
  kind: "init" | "get" | "set";
  method: boolean;
  shorthand: boolean;
  computed: boolean;
}

//REVIEW Does property contain method ??
export interface PropertyDefinition extends BaseNode {
    type: "PropertyDefinition";
    key: Expression | PrivateIdentifier;
    value?: Expression | null | undefined;
    computed: boolean;
    static: boolean;
}

//REVIEW In which case does id be null ?
export interface FunctionExpression extends BaseFunction, BaseExpression {
  id?: Identifier | null | undefined;
  type: "FunctionExpression";
  body: BlockStatement;
}

export interface SequenceExpression extends BaseExpression {
  type: "SequenceExpression";
  expressions: Array<Expression>;
}

//REVIEW
export interface UnaryExpression extends BaseExpression {
  type: "UnaryExpression";
  operator: UnaryOperator;
  prefix: true;
  argument: Expression;
}

//REVIEW
export interface BinaryExpression extends BaseExpression {
  type: "BinaryExpression";
  operator: BinaryOperator;
  left: Expression;
  right: Expression;
}

export interface AssignmentExpression extends BaseExpression {
  type: "AssignmentExpression";
  operator: AssignmentOperator;
  left: Pattern | MemberExpression;
  right: Expression;
}

//REVIEW
export interface UpdateExpression extends BaseExpression {
  type: "UpdateExpression";
  operator: UpdateOperator;
  argument: Expression;
  prefix: boolean;
}


//REVIEW
export interface LogicalExpression extends BaseExpression {
  type: "LogicalExpression";
  operator: LogicalOperator;
  left: Expression;
  right: Expression;
}

//REVIEW
export interface ConditionalExpression extends BaseExpression {
  type: "ConditionalExpression";
  test: Expression;
  alternate: Expression;
  consequent: Expression;
}

//REVIEW
interface BaseCallExpression extends BaseExpression {
  callee: Expression | Super;
  arguments: Array<Expression | SpreadElement>;
}

//REVIEW
export type CallExpression = SimpleCallExpression | NewExpression;

//REVIEW
export interface SimpleCallExpression extends BaseCallExpression {
  type: "CallExpression";
  optional: boolean;
}

//REVIEW
export interface NewExpression extends BaseCallExpression {
  type: "NewExpression";
}

//REVIEW
export interface MemberExpression extends BaseExpression, BasePattern {
  type: "MemberExpression";
  object: Expression | Super;
  property: Expression | PrivateIdentifier;
  computed: boolean;
  optional: boolean;
}

//TODO Shall group corresponding  rules in ANTLR into Pattern ? 
export type Pattern =
    Identifier | ObjectPattern | ArrayPattern | RestElement |
    AssignmentPattern | MemberExpression;

interface BasePattern extends BaseNode { }

//REVIEW
export interface SwitchCase extends BaseNode {
  type: "SwitchCase";
  test?: Expression | null | undefined;
  consequent: Array<Statement>;
}

//REVIEW
export interface CatchClause extends BaseNode {
  type: "CatchClause";
  param: Pattern | null;
  body: BlockStatement;
}

//REVIEW
export interface Identifier extends BaseNode, BaseExpression, BasePattern {
  type: "Identifier";
  name: string;
}

//REVIEW
export type Literal = SimpleLiteral | RegExpLiteral | BigIntLiteral;

//REVIEW
export interface SimpleLiteral extends BaseNode, BaseExpression {
  type: "Literal";
  value: string | boolean | number | null;
  raw?: string | undefined;
}

//REVIEW
export interface RegExpLiteral extends BaseNode, BaseExpression {
  type: "Literal";
  value?: RegExp | null | undefined;
  regex: {
    pattern: string;
    flags: string;
  };
  raw?: string | undefined;
}

//TODO shall distinguish bigInt in ANTLR  ??
export interface BigIntLiteral extends BaseNode, BaseExpression {
  type: "Literal";
  value?: bigint | null | undefined;
  bigint: string;
  raw?: string | undefined;
}

//REVIEW
export type UnaryOperator =
    "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";

//REVIEW   
export type BinaryOperator =
    "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" |
    ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "**" | "|" | "^" | "&" | "in" |
    "instanceof";

//REVIEW
export type LogicalOperator = "||" | "&&" | "??";

//REVIEW
export type AssignmentOperator =
    "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "<<=" | ">>=" | ">>>=" |
    "|=" | "^=" | "&=";

//REVIEW
export type UpdateOperator = "++" | "--";

export interface ForOfStatement extends BaseForXStatement {
  type: "ForOfStatement";
  await: boolean;
}

//REVIEW
export interface Super extends BaseNode {
  type: "Super";
}

export interface SpreadElement extends BaseNode {
  type: "SpreadElement";
  argument: Expression;
}

/**
An arrow function expression is a compact alternative to a traditional function expression, but is limited and can't be used in all situations.
Differences & Limitations:

Does not have its own bindings to this or super, and should not be used as methods.
Does not have new.target keyword.
Not suitable for call, apply and bind methods, which generally rely on establishing a scope.
Can not be used as constructors.
Can not use yield, within its body.
 */
export interface ArrowFunctionExpression extends BaseExpression, BaseFunction {
  type: "ArrowFunctionExpression";
  expression: boolean;
  body: BlockStatement | Expression;
}

//REVIEW
/**
 * Can be used in the generator function only
 * 
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
 * 
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
 */
export interface YieldExpression extends BaseExpression {
  type: "YieldExpression";
  argument?: Expression | null | undefined;
  delegate: boolean;
}

//TODO
export interface TemplateLiteral extends BaseExpression {
  type: "TemplateLiteral";
  quasis: Array<TemplateElement>;
  expressions: Array<Expression>;
}

/**
 * e.g.  const coloredString = blue`{name}`
 * 
 * https://wesbos.com/tagged-template-literals
 */
export interface TaggedTemplateExpression extends BaseExpression {
  type: "TaggedTemplateExpression";
  tag: Expression;
  quasi: TemplateLiteral;
}

export interface TemplateElement extends BaseNode {
  type: "TemplateElement";
  tail: boolean;
  value: {
    /** It is null when the template literal is tagged and the text has an invalid escape (e.g. - tag`\unicode and \u{55}`) */
    cooked?: string | null | undefined;
    raw: string;
  };
}

export interface AssignmentProperty extends Property {
  value: Pattern;
  kind: "init";
  method: boolean; // false
}

export interface ObjectPattern extends BasePattern {
  type: "ObjectPattern";
  properties: Array<AssignmentProperty | RestElement>;
}

export interface ArrayPattern extends BasePattern {
  type: "ArrayPattern";
  elements: Array<Pattern | null>;
}

export interface RestElement extends BasePattern {
  type: "RestElement";
  argument: Pattern;
}

export interface AssignmentPattern extends BasePattern {
  type: "AssignmentPattern";
  left: Pattern;
  right: Expression;
}

export type Class = ClassDeclaration | ClassExpression;
interface BaseClass extends BaseNode {
  superClass?: Expression | null | undefined;
  body: ClassBody;
}

export interface ClassBody extends BaseNode {
  type: "ClassBody";
  body: Array<MethodDefinition | PropertyDefinition>;
}

export interface MethodDefinition extends BaseNode {
  type: "MethodDefinition";
  key: Expression | PrivateIdentifier;
  value: FunctionExpression;
  kind: "constructor" | "method" | "get" | "set";
  computed: boolean;
  static: boolean;
}

export interface ClassDeclaration extends BaseClass, BaseDeclaration {
  type: "ClassDeclaration";
  /** It is null when a class declaration is a part of the `export default class` statement */
  id: Identifier | null;
}

export interface ClassExpression extends BaseClass, BaseExpression {
  type: "ClassExpression";
  id?: Identifier | null | undefined;
}

/**
 * Details see https://tc39.es/ecma262/
 * 
13.3.12.1 Runtime Semantics: Evaluation
NewTarget : new . target
1. Return GetNewTarget().
ImportMeta : import . meta
1. Let module be ! GetActiveScriptOrModule().
2. Assert: module is a Source Text Module Record.
3. Let importMeta be module.[[ImportMeta]].
4. If importMeta is empty, then
a. Set importMeta to ! OrdinaryObjectCreate(null).
b. Let importMetaValues be ! HostGetImportMetaProperties(module).
c. For each Record { [[Key]], [[Value]] } p of importMetaValues, do
i. Perform ! CreateDataPropertyOrThrow(importMeta, p.[[Key]], p.[[Value]]).
d. Perform ! HostFinalizeImportMeta(importMeta, module).
e. Set module.[[ImportMeta]] to importMeta.
f. Return importMeta.
5. Else,
a. Assert: Type(importMeta) is Object.
b. Return importMeta.
13.3.12.1.1 HostGetImportMetaProperties ( moduleRecord )
The host-defined abstract operation HostGetImportMetaProperties takes argument moduleRecord (a Module Record). It allows hosts to provide property keys and values for the object returned from import.meta.

An implementation of HostGetImportMetaProperties must conform to the following requirements:

It must return a normal completion with a value of a List whose values are all Records with two fields, [[Key]] and [[Value]].
Each such Record's [[Key]] field must be a property key, i.e., IsPropertyKey must return true when applied to it.
Each such Record's [[Value]] field must be an ECMAScript language value.
The default implementation of HostGetImportMetaProperties is to return NormalCompletion(« »).

13.3.12.1.2 HostFinalizeImportMeta ( importMeta, moduleRecord )
The host-defined abstract operation HostFinalizeImportMeta takes arguments importMeta (an Object) and moduleRecord (a Module Record). It allows hosts to perform any extraordinary operations to prepare the object returned from import.meta.

Most hosts will be able to simply define HostGetImportMetaProperties, and leave HostFinalizeImportMeta with its default behaviour. However, HostFinalizeImportMeta provides an "escape hatch" for hosts which need to directly manipulate the object before it is exposed to ECMAScript code.

An implementation of HostFinalizeImportMeta must conform to the following requirements:

It must complete normally (i.e., not return an abrupt completion).
The default implementation of HostFinalizeImportMeta is to return NormalCompletion(empty).
 */
export interface MetaProperty extends BaseExpression {
  type: "MetaProperty";
  meta: Identifier;
  property: Identifier;
}

export type ModuleDeclaration =
    ImportDeclaration | ExportNamedDeclaration | ExportDefaultDeclaration |
    ExportAllDeclaration;
interface BaseModuleDeclaration extends BaseNode { }

export type ModuleSpecifier =
    ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier |
    ExportSpecifier;
interface BaseModuleSpecifier extends BaseNode {
  local: Identifier;
}

export interface ImportDeclaration extends BaseModuleDeclaration {
  type: "ImportDeclaration";
  specifiers: Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>;
  source: Literal;
}

export interface ImportSpecifier extends BaseModuleSpecifier {
  type: "ImportSpecifier";
  imported: Identifier;
}

export interface ImportExpression extends BaseExpression {
  type: "ImportExpression";
  source: Expression;
}

export interface ImportDefaultSpecifier extends BaseModuleSpecifier {
  type: "ImportDefaultSpecifier";
}

export interface ImportNamespaceSpecifier extends BaseModuleSpecifier {
  type: "ImportNamespaceSpecifier";
}

export interface ExportNamedDeclaration extends BaseModuleDeclaration {
  type: "ExportNamedDeclaration";
  declaration?: Declaration | null | undefined;
  specifiers: Array<ExportSpecifier>;
  source?: Literal | null | undefined;
}

export interface ExportSpecifier extends BaseModuleSpecifier {
  type: "ExportSpecifier";
  exported: Identifier;
}

export interface ExportDefaultDeclaration extends BaseModuleDeclaration {
  type: "ExportDefaultDeclaration";
  declaration: Declaration | Expression;
}

export interface ExportAllDeclaration extends BaseModuleDeclaration {
  type: "ExportAllDeclaration";
  exported: Identifier | null;
  source: Literal;
}

export interface AwaitExpression extends BaseExpression {
  type: "AwaitExpression";
  argument: Expression;
}
